
/**
 * Universidad del Valle de Guatemala
 * CC2003 Algoritmos y Estructuras de Datos
 * Proyecto 1: Intérprete de LISP --> Lambda LISP
 * @version 2 Septiembre, 2009
 * Integrantes:
 *      @author Julio Chicas, José García, Ernesto Rodríguez, Karen Tojín
 **/
/**
 * Descripción: Parsing de un ingreso del usuario a una lista o a realizar las operaciones indicadas
 * cuando sea el caso.
 **/
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;

public class Parser {

    /**
     * Método que realiza el parsing del String ingresado por el usuario
     * @param lista: El String ingresado
     * @return La lista parseada
     * @throws Exception
     */
    public static List parse(String lista) throws Exception {

        List objs = new Lista();
        boolean islist = false;
        boolean isNlist = false;

        Scanner scn = new Scanner(prepare(lista));

        String cache;
        cache = scn.next();

        if (!(cache.equals("(") || cache.equals("'") || cache.equals("`"))) {
            throw new Exception();
        }

        if (!cache.equals("(")) {
            islist = true;
            isNlist = true;
        }
        do {
            try {
                cache = scn.next();
            } catch (NoSuchElementException e) {

                if (isNlist) {

                    return (List) objs.get(0);
                }
                throw e;
            }

            if (!cache.equals(")")) {

                if (cache.equals("(") || cache.equals("'") || cache.equals("`")) {

                    String tmp;
                    int par = 1;

                    if (!cache.equals("(")) {

                        islist = true;
                        cache = "(";
                        if (!scn.next().equals("(")) {
                            throw new Exception();
                        }
                    }

                    do {
                        tmp = scn.next();
                        cache = cache + " " + tmp;
                        if (tmp.equals(")")) {
                            par--;
                        }

                        if (tmp.equals("(")) {
                            par++;
                        }

                    } while (!tmp.equals(")") || !(par == 0));

                    List tmp1 = parse(cache);

                    if (islist) {
                        tmp1.add(0, "'");
                        islist = false;
                    }
                    objs.add(tmp1);
                } else {

                    try {
                        objs.add(Float.parseFloat(cache));
                    } catch (NumberFormatException e) {
                        objs.add(cache);
                    }
                }
            }
        } while (!cache.equals(")"));
        return objs;

    }

    /**
     * Método que identifica parte la sintaxis de lo ingresado
     * @param data: Ingreso del Usuario
     * @return Un String con una preevaluación realizada
     */
    public static String prepare(String data) {

        String cache = "";

        for (int veces = 0; veces < data.length(); veces++) {

            if (data.charAt(veces) == '(') {

                cache = cache + " ( ";
            } else if (data.charAt(veces) == ')') {

                cache = cache + " ) ";
            } else if (data.charAt(veces) == '`' || data.charAt(veces) == 39) {

                cache = cache + " '";
            } else {
                cache = cache + data.charAt(veces);
            }
        }
        return cache;
    }

    /**
     * Método que se utiliza para la definición de funciones
     * @param lista: El String ingresado
     * @return La lista parseada
     * @throws Exception
     */
    public static Lista<String> simpleParse(String lista) throws Exception {
        lista = prepare(lista);
        Lista objs = new Lista();

        int cont = 0;
        Scanner scn = new Scanner(lista);
        String cache = "";

        if (!scn.next().equals("(")) {
            throw new Exception();
        }
        do {
            cache = scn.next();
            if (!cache.equals(")")) {
                if (cache.equals("(")) {
                    cont++;
                    cache = " ( ";
                    String tmp = "";
                    do {
                        tmp = scn.next();
                        if (tmp.equals("(")) {
                            cont++;
                        }
                        if (tmp.equals(")")) {
                            cont--;
                        }
                        cache = cache + " " + tmp + " ";


                    } while (!tmp.equals(")") || cont != 0);
                }
                objs.add(cache);
            }

        } while (!cache.equals(")"));

        return objs;
    }
}
